package 泛型;

import java.util.*;
/*
为了提高可读性，用有意义的字母比较好，一般来讲，在不同的情境下使用的字母意义如下：
E — Element，常用在java Collection里，如：List<E>,Iterator<E>,Set<E>
K,V — Key，Value，代表Map的键值对
N — Number，数字
T — Type，类型，如String，Integer等等

① 普通成员可以使用泛型(属性、方法)
② 使用泛型的数组，不能初始化
③ 静态成员不能使用类的泛型
④ 泛型类的类型，是在创建对象时确定的(因为创建对象时，需要指定泛型的类型)
⑤ 如果在创建对象时没有指定泛型类型，默认为Object

使用案例：

class Tiger<T, R, M> {
	T ttt;
	R rrr;
	M mmm;
	
	T[] tArr;	// 不能初始化，不能确定类型，就无法在内存中开辟空间

	// static T t;	// 静态成员无法使用类的泛型
	// static void setT(T t){}	// 因为静态成员是和类相关的
	// 在类没有创建对象时就可以调用，而泛型是在创建对象时指定的
	// 所以静态成员无法使用类的泛型，否则JVM则无法完成初始化
	
	// 普通成员
   public void setM(M m) {
       this.mmm = m;
  }
    
   public M getM() {
       return mmm;
  }
}

使用泛型类：
Tiger<String, Integer, Object> tiger = new Tiger<>();

// 没有指定，就默认指定为Object
Tiger tiger = new Tiger();
// 等价于:Tiger<Object, Object, Object> tiger = new Tiger<>();

【自定义泛型方法】
语法格式：

修饰符 <T, R...> 返回类型 方法名(参数列表) {	// ...表示可以有多个泛型
	// 方法体
}

细节：

① 泛型方法可以定义在普通类中，也可以定义在泛型类中
② 当泛型方法被调用时，泛型类型会确定
③ public void eat(E e){}，因为修饰符后没有<T, R...>，所以，该方法不是泛型方法，而是使用了泛型。

使用案例：

// 普通类
class Cat {
    
    // 泛型方法
    // <T, R> 就是泛型，只给该方法使用
    public <T, R> void fly(T t, R r) {
    }
    
}

// 泛型类
class Dog<T, R> {

	// 普通方法
	public void eat() {
	}
    
    // 泛型方法
    // <A, E> 就是泛型，只给该方法使用
    // 尽量保持不和类泛型一致，提高代码阅读
    public <A, E> void fly(A a, E e) {
    }

	// 泛型方法
	// 泛型方法既可以使用自定义的泛型，也可以使用类定义的泛型
	// B 使用了方法定义的泛型，R 使用了类定义的泛型
	public <B> void sleep(B b, R r){
	}

	// 使用了泛型的方法
	// 该方法不是泛型方法，是方法使用了类定义的泛型
	public void run(T t){
	}
    
}

使用泛型方法：

Cat cat = new Cat();
cat.fly("宝马", 100);	// 在调用泛型方法时，编译器会确定传入参数的类型

Dog<String, Integer> dog = new Dog<>();
dog.sleep("晚上睡", "白天起");	// 此时，该方法的 B 和 R 泛型都是String类型
dog.run("跑");	// 因为已经指定了类泛型 T 的类型，所以这里的参数只能是String类型

【泛型通配符和继承性】
泛型不具备继承性，比如：

// 这样写是错的，泛型没有继承性
List<Object> list = new ArrayList<String>();

泛型的通配符： 不知道使用什么类型来接收的时候，此时可以使用?，?表示未知通配符。

当使用泛型类或者接口时，传递的数据中，泛型类型不确定，可以通过通配符<?>表示。
但是一旦使用泛型的通配符后，只能使用Object类中的共性方法，集合中元素自身方法无法使用。

语法格式：
<?>：支持任意泛型类型
<? extends A>：支持A类以及A类的子类，规定了泛型的上限
<? super A>：支持A类以及A类的父类，规定了泛型的下限

使用案例：
比如：现已知Object类，String 类，Number类，Integer类，其中Number是Integer的父类

class Test {
	public static void main(String[] args) {
	   Collection<Integer> list1 = new ArrayList<Integer>();
	   Collection<String> list2 = new ArrayList<String>();
	   Collection<Number> list3 = new ArrayList<Number>();
	   Collection<Object> list4 = new ArrayList<Object>();
	   
	   getElement(list1);
	   getElement(list2);//报错
	   getElement(list3);
	   getElement(list4);//报错
	 
	   getElement2(list1);//报错
	   getElement2(list2);//报错
	   getElement2(list3);
	   getElement2(list4);
	 
	}
	// 泛型的上限：此时的泛型?，必须是Number类型或者Number类型的子类
	public static void getElement(Collection<? extends Number> coll){}
	// 泛型的下限：此时的泛型?，必须是Number类型或者Number类型的父类
	public static void getElement2(Collection<? super Number> coll){}

}



【泛型类：】
泛型类的声明和非泛型类的声明类似，除了在类名后面添加了类型参数声明部分。和泛型方法一样，泛型类的类型参数声明部分也包含一个或多个类型参数，参数间用逗号隔开。
一个泛型参数，也被称为一个类型变量，是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数，这些类被称为参数化的类或参数化的类型。

如下实例演示了我们如何定义一个泛型类:

public class Box<T> {
 
  private T t;
 
  public void add(T t) {
    this.t = t;
  }
 
  public T get() {
    return t;
  }
 
  public static void main(String[] args) {
     Box<Integer> integerBox = new Box<Integer>();
     Box<String> stringBox = new Box<String>();
   
     integerBox.add(new Integer(10));
     stringBox.add(new String("Hello World"));
 
     System.out.printf("Integer Value :%d\n\n", integerBox.get());
     System.out.printf("String Value :%s\n", stringBox.get());
  }
}*/

/*多个泛型类型
泛型还可以定义多种类型。例如，我们希望Pair不总是存储两个类型一样的对象，就可以使用类型<T, K>：

public class Pair<T, K> {
    private T first;
    private K last;
    public Pair(T first, K last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public K getLast() { ... }
}
使用的时候，需要指出两种类型：

Pair<String, Integer> p = new Pair<>("test", 123);
Java标准库的Map<K, V>就是使用两种泛型类型的例子。它对Key使用一种类型，对Value使用另一种类型。*/

/*泛型方法
你可以写一个泛型方法，该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型，编译器适当地处理每一个方法调用。
下面是定义泛型方法的规则：

所有泛型方法声明都有一个类型参数声明部分（由尖括号分隔），该类型参数声明部分在方法返回类型之前（在下面例子中的<E>）。
每一个类型参数声明部分包含一个或多个类型参数，参数间用逗号隔开。一个泛型参数，也被称为一个类型变量，是用于指定一个泛型类型名称的标识符。
类型参数能被用来声明返回值类型，并且能作为泛型方法得到的实际参数类型的占位符。
泛型方法方法体的声明和其他方法一样。注意类型参数只能代表引用型类型，不能是原始类型（像 int,double,char 的等）。
下面的例子演示了如何使用泛型方法打印不同字符串的元素：

public class GenericMethodTest
{
   // 泛型方法 printArray                         
   public static < E > void printArray( E[] inputArray )
   {
      // 输出数组元素            
         for ( E element : inputArray ){        
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }

    public static void main( String args[] )
    {
        // 创建不同类型数组： Integer, Double 和 Character
        Integer[] intArray = { 1, 2, 3, 4, 5 };
        Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

        System.out.println( "Array integerArray contains:" );
        printArray( intArray  ); // 传递一个整型数组

        System.out.println( "\nArray doubleArray contains:" );
        printArray( doubleArray ); // 传递一个双精度型数组

        System.out.println( "\nArray characterArray contains:" );
        printArray( charArray ); // 传递一个字符型型数组
    } 
}
*/

//类型通配符

//1、类型通配符一般是使用?代替具体的类型参数。例如 List<?> 在逻辑上是List<String>,List<Integer> 等所有List<具体类型实参>的父类。

//import java.util.*;

/*public class 编写泛型 {
    
   public static void main(String[] args) {
       List<String> name = new ArrayList<>();//List<String> name = new ArrayList<String>();省略后面尖括号中的String
       List<Integer> age = new ArrayList<>();
       List<Number> number = new ArrayList<>();
       
       name.add("icon");
       age.add(18);
//        age.add(20);
//        System.out.println(age.get(0)+age.get(1));
       number.add(314);

       getData(name);
       getData(age);
       getData(number);
      
  }

//   public static void getData(List<? extends Number> data) {//不适应String
//      System.out.println("data :" + data.get(0));
//   }
  public static <T> void getData(List<?> data) {
   System.out.println("data :" + (data.get(0)));
  }
}*/

//解析： 因为getData()方法的参数是List类型的，所以name，age，number都可以作为这个方法的实参，这就是通配符的作用

//2、类型通配符上限通过形如List来定义，如此定义就是通配符泛型值接受Number及其下层子类类型。

public class 编写泛型 {

	public static void main(String[] args) {
		List<String> name = new ArrayList<String>();
		List<Integer> age = new ArrayList<Integer>();
		List<Long> age1 = new ArrayList<Long>();
		List<Number> number = new ArrayList<Number>();

		name.add("icon");
		age.add(18);
		age1.add((long) 18);
		number.add(314);

		// getUperNumber(name);//1
		getData(name);
		getUperNumber(age);// 2
		getUperNumber(age1);// 2
		getUperNumber(number);// 3

		//ArrayList可以包含不同类型的数据，使用时必须强制转型（或类型声明）
		ArrayList list=new ArrayList();
		list.add(123);
		System.out.println(list.get(0));
		list.add("gjj");
		System.out.println(list.get(1));
		System.out.println(list);
		Long x=(Long)list.get(0)+1;
//		int x=(Integer)list.get(0)+1;
		System.out.println(x);
		String y=(String)list.get(1)+"aaa";
		System.out.println(y);
		
	}

	public static void getData(List<?> data) {
		System.out.println("data :" + data.get(0));
	}

	public static void getUperNumber(List<? extends Number> data) {
		System.out.println("data :" + data.get(0));
	}
}

//data :icon
//data :18
//data :314
//解析： 在(//1)处会出现错误，因为getUperNumber()方法中的参数已经限定了参数泛型上限为Number，所以泛型为String是不在这个范围之内，所以会报错

//3、类型通配符下限通过形如 List<? super Number>来定义，表示类型只能接受Number及其三层父类类型，如 Object 类型的实例。
